Animation: When and Why?
Introducing the Tools
Hands-On Tutorial
June 2019
Animation: When and Why?
Introducing the Tools
Hands-On Tutorial
What are some reasons to animate a plot?
Also: If you need to see all the data points at the same time DON’T use animation
library(ggplot2) # make plots library(gganimate) # animate the plots library(gifski) # render gifs library(transformr) # insert smooth transformations
Using data on displaced persons living in South Africa, show patterns and trends in refugee movement over twenty-plus years.
Task: show complex data across many levels of a facet
Problems: Facet grid and facet wrap rapidly get out of their depth
Data Source: UNHCR via data.world: UNHCR’s populations of concern residing in South Africa
Grab dataset of displaced persons living in South Africa
| X_date_year | X_country_residence | X_country_origin | X_population_type | X_affected |
|---|---|---|---|---|
| 1993 | South Africa | Mozambique | Refugees (incl. refugee-like situations) | 250000 |
| 1994 | South Africa | Angola | Refugees (incl. refugee-like situations) | 581 |
| 1994 | South Africa | Dem. Rep. of the Congo | Refugees (incl. refugee-like situations) | 808 |
| 1994 | South Africa | Ethiopia | Refugees (incl. refugee-like situations) | 66 |
Select the top 10 countries of origin for each year, apply some filters.
library(magrittr) library(dplyr) plotDT <- dtset %>% filter(X_country_origin != "Various/Unknown") %>% filter(X_population_type == "Refugees (incl. refugee-like situations)") %>% filter(X_date_year > 1995) %>% group_by(X_date_year) %>% mutate(rank = rank(-X_affected, ties.method = "first") * 1) %>% ungroup() %>% filter(rank <= 10) %>% data.frame()
| X_date_year | X_country_residence | X_country_origin | X_population_type | X_affected | rank | state_time |
|---|---|---|---|---|---|---|
| 1996 | South Africa | Angola | Refugees (incl. refugee-like situations) | 3876 | 1 | 1 |
| 1996 | South Africa | Bangladesh | Refugees (incl. refugee-like situations) | 452 | 9 | 1 |
| 1996 | South Africa | China | Refugees (incl. refugee-like situations) | 469 | 8 | 1 |
| 1996 | South Africa | Dem. Rep. of the Congo | Refugees (incl. refugee-like situations) | 2505 | 3 | 1 |
baseplot1 <- ggplot(plotDT,
aes(X_date_year,
group = X_country_origin,
fill = as.factor(X_country_origin),
color = as.factor(X_country_origin)))+
theme_bw()+
theme(legend.position = "bottom")+
geom_bar(aes(y = X_affected), stat = "identity", position = "dodge")
Ew.
baseplot2 <- baseplot1 +
coord_flip(clip = "off", expand = FALSE, ylim = c(0, 50000))
baseplot2 <- baseplot2 +
geom_text(aes(y = 0, label = paste(X_country_origin, " ")), vjust = 0.2, hjust = 1) +
geom_text(aes(y = X_affected,
label = as.character(X_affected)),
color = "black", vjust = 0.2, hjust = -.5)
Can’t use the same stub now.
baseplot3 <- ggplot(plotDT,
aes(x=rank,
group = X_country_origin,
fill = as.factor(X_country_origin),
color = as.factor(X_country_origin)))+
theme_bw()+
theme(legend.position = "bottom")+
geom_text(aes(y = 0, label = paste(X_country_origin, " ")), vjust = 0.2, hjust = 1) +
geom_text(aes(y = X_affected,
label = as.character(X_affected)),
color = "black", vjust = 0.2, hjust = -.5)+
coord_flip(clip = "off", expand = FALSE, ylim = c(0, 50000)) +
geom_bar(aes(y = X_affected), stat = "identity", position = "identity")
Left side: grouped bar, sorted by year
Right side: not grouped bar, sorted by rank
If your stub has a theme() segment, applying a new one will overrule it.
baseplot3 <- baseplot3 +
theme(legend.position = "none",
axis.ticks.y = element_blank(), # These relate to the axes post-flip
axis.text.y = element_blank(), # These relate to the axes post-flip
axis.title.y = element_blank(),
plot.margin = margin(1,1,1,5, "cm"))
baseplot3 <- baseplot3 + scale_y_continuous(labels = scales::comma) + scale_x_reverse()
Now we have the different “frames” all layered on top of each other.
We need to present the data on an annual basis in a way that tells us:
No.
Definitely not.
animp <- baseplot3 +
transition_states(
X_date_year)
Added a descriptive title/label that indicate the year of the frame
animp <- baseplot3 +
labs(title = "Refugees Residing in South Africa by Origin, {closest_state}"
, y="Affected Persons")+
transition_states(X_date_year)
Shrink and grow on exit and enter
animp <- baseplot3 +
labs(title = "Refugees Residing in South Africa by Origin, {closest_state}"
, y="Affected Persons")+
transition_states(X_date_year)+
enter_grow() +
exit_shrink()
Interesting, but probably not serving the project objectives
Ease between positions (moving on page, not exiting or entering)
animp <- baseplot3 +
labs(title = "Refugees Residing in South Africa by Origin, {closest_state}"
, y="Affected Persons")+
transition_states(X_date_year)+
ease_aes('quartic-in-out')
Hopefully, the movement feels less abrasive to the eye
For fun, let’s try “back” to see a springier approach
animp <- baseplot3 +
labs(title = "Refugees Residing in South Africa by Origin, {closest_state}"
, y="Affected Persons")+
transition_states(X_date_year)+
ease_aes('back-in-out')
Feels a little cartoony- interesting, but again perhaps not what we need
animp <- baseplot3 +
labs(title = "Refugees Residing in South Africa by Origin, {closest_state}"
, y="Affected Persons")+
transition_states(
X_date_year,
transition_length = 4,
state_length = c(rep(3, 21), 30),
wrap = FALSE)+
ease_aes('quartic-in-out')+
enter_fade() +
exit_fade()
Slower pace feels smoother
transition_states(
X_date_year,
transition_length = 4,
state_length = c(rep(1, 21), 30)
, wrap = FALSE)
Assign the frame unit- here we use the year.
Transition length is the period of time we use for change from frame to frame
- slow it down if you want a smooth looking animation
State length is the period of time where the frame stays static.
- here I am making the very last frame stay static longer
Wrap determines whether to apply transition smoothing between the end and restarting
Experiment with these settings to get the look you want!
animate(animp, fps = 10, duration = 20, width = 700, height = 400)
anim_save(filename = "final_race_plot.gif")
Figure out the in/out transitions that make sense and are not misleading/distracting
Give the eye enough time to understand each state
Make your plot serve the audience, don’t be fancy if it’s not helpful